package cn.geminis.workflow.service.controller;

import cn.geminis.data.jpa.PageQuery;
import cn.geminis.workflow.service.controller.vo.Model;
import cn.geminis.workflow.service.utils.ProcessQueryUtils;
import cn.geminis.workflow.service.utils.QueryUtils;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.engine.ProcessEngine;
import org.springframework.data.domain.Page;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.xml.stream.XMLInputFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.List;

/**
 * @author puddi
 */
@RestController
@RequestMapping("/model")
@RequiredArgsConstructor
public class ModelController {

    private final ProcessEngine engine;

    @GetMapping
    public Page<Model> findPage(final PageQuery pageParameter, String category) {
        var query = engine
                .getRepositoryService()
                .createProcessDefinitionQuery()
                .latestVersion();

        ProcessQueryUtils.filterCategory(query, category);

        return QueryUtils.toPage(query, pageParameter, Model.class);
    }

    @GetMapping("/{processKey}/bpmn")
    public void geXml(@PathVariable String processKey, HttpServletResponse response) {
        var process = this.engine
                .getRepositoryService()
                .getProcessDefinition(processKey);

        try (var stream = this.engine
                .getRepositoryService()
                .getResourceAsStream(process.getDeploymentId(), process.getResourceName())) {
            stream.transferTo(response.getOutputStream());
        } catch (IOException e) {
            throw new RuntimeException("读取流程BPMN内容错误", e);
        }

        response.setHeader("Content-Disposition", "attachment; filename=" + processKey + ".bpmn");
        response.setContentType(MediaType.APPLICATION_XML_VALUE);
    }

    @PutMapping
    @SneakyThrows
    public void save(String category, @RequestBody String xml) {
        try (var inputStream = new ByteArrayInputStream(xml.getBytes())) {
            var factory = XMLInputFactory.newInstance();
            factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, true);
            var reader = factory.createXMLStreamReader(inputStream);
            var converter = new BpmnXMLConverter();
            var bpmnModel = converter.convertToBpmnModel(reader);
            bpmnModel.setTargetNamespace(category);
            var process = bpmnModel.getMainProcess();

            engine.getRepositoryService()
                    .createDeployment()
                    .category(category)
                    .key(process.getId())
                    .name(process.getName())
                    .enableDuplicateFiltering()
                    //需要以后缀.bpmn定义资源名称，否则无法创建procDef
                    .addBpmnModel(process.getId() + ".bpmn", bpmnModel)
                    .deploy();
        }
    }

    @PostMapping("/import/{category}")
    public void importModel(@PathVariable String category, MultipartFile file) {
        try {
            this.save(new String(Base64.getDecoder().decode(category)), new String(file.getBytes()));
        } catch (IOException e) {
            throw new RuntimeException("导入流程失败", e);
        }
    }

    @DeleteMapping
    public void delete(@RequestBody List<String> ids) {
        ids.forEach(id -> this.engine.getRepositoryService().deleteDeployment(id, true));
    }

}
